System.Span<T-Struktur>

Dieser Artikel enthält ergänzende Hinweise zur Referenzdokumentation für diese API.

Der Span<T> Typ ist eine Referenzstruktur , die auf dem Stapel zugewiesen wird, anstatt auf dem verwalteten Heap. Verweistypen weisen eine Reihe von Einschränkungen auf, um sicherzustellen, dass sie nicht zum verwalteten Heap heraufgestuft werden können, einschließlich, dass sie nicht boxen können, sie können variablen vom Typ Objectdynamic oder einem beliebigen Schnittstellentyp nicht zugewiesen werden, sie können keine Felder in einem Verweistyp sein, und sie können nicht über Grenzen hinweg awaityield verwendet werden. Darüber hinaus rufen Sie zwei Methoden auf und Equals(Object)GetHashCodelösen einen NotSupportedException.

Wichtig

Da es sich um einen schreibgeschützten Typ handelt, ist es für viele Szenarien nicht geeignet, Span<T> in denen Verweise auf Puffer im Heap gespeichert werden müssen. Dies gilt beispielsweise für Routinen, die asynchrone Methodenaufrufe ausführen. Für solche Szenarien können Sie die ergänzenden System.Memory<T> Und System.ReadOnlyMemory<T> Typen verwenden.

Verwenden Sie System.ReadOnlySpan<T>für Spannen, die unveränderliche oder schreibgeschützte Strukturen darstellen.

Arbeitsspeicher

A Span<T> stellt einen zusammenhängenden Bereich des beliebigen Speichers dar. Eine Span<T> Instanz wird häufig verwendet, um die Elemente eines Arrays oder eines Teils eines Arrays zu enthalten. Im Gegensatz zu einem Array kann eine Span<T> Instanz jedoch auf verwalteten Speicher, systemeigenen Speicher oder auf den im Stapel verwalteten Speicher verweisen. Im folgenden Beispiel wird ein Span<Byte> Array erstellt:

// Create a span over an array.
var array = new byte[100];
var arraySpan = new Span<byte>(array);

byte data = 0;
for (int ctr = 0; ctr < arraySpan.Length; ctr++)
    arraySpan[ctr] = data++;

int arraySum = 0;
foreach (var value in array)
    arraySum += value;

Console.WriteLine($"The sum is {arraySum}");
// Output:  The sum is 4950
// Create a span over an array.
let array = Array.zeroCreate<byte> 100
let arraySpan = Span<byte> array

let mutable data = 0uy
for i = 0 to arraySpan.Length - 1 do
    arraySpan[i] <- data
    data <- data + 1uy

let mutable arraySum = 0
for value in array do
    arraySum <- arraySum + int value

printfn $"The sum is {arraySum}"
// Output:  The sum is 4950

Im folgenden Beispiel wird ein Span<Byte> von 100 Bytes systemeigener Arbeitsspeicher erstellt:

// Create a span from native memory.
var native = Marshal.AllocHGlobal(100);
Span<byte> nativeSpan;
unsafe
{
    nativeSpan = new Span<byte>(native.ToPointer(), 100);
}
byte data = 0;
for (int ctr = 0; ctr < nativeSpan.Length; ctr++)
    nativeSpan[ctr] = data++;

int nativeSum = 0;
foreach (var value in nativeSpan)
    nativeSum += value;

Console.WriteLine($"The sum is {nativeSum}");
Marshal.FreeHGlobal(native);
// Output:  The sum is 4950
// Create a span from native memory.
let native = Marshal.AllocHGlobal 100
let nativeSpan = Span<byte>(native.ToPointer(), 100)

let mutable data = 0uy
for i = 0 to nativeSpan.Length - 1 do
    nativeSpan[i] <- data
    data <- data + 1uy

let mutable nativeSum = 0
for value in nativeSpan do
    nativeSum <- nativeSum + int value

printfn $"The sum is {nativeSum}"
Marshal.FreeHGlobal native
// Output:  The sum is 4950

Im folgenden Beispiel wird der C#-stackalloc-Schlüsselwort (keyword) verwendet, um 100 Bytes Arbeitsspeicher auf dem Stapel zuzuweisen:

// Create a span on the stack.
byte data = 0;
Span<byte> stackSpan = stackalloc byte[100];
for (int ctr = 0; ctr < stackSpan.Length; ctr++)
    stackSpan[ctr] = data++;

int stackSum = 0;
foreach (var value in stackSpan)
    stackSum += value;

Console.WriteLine($"The sum is {stackSum}");
// Output:  The sum is 4950
    // Create a span on the stack.
    let mutable data = 0uy
    let stackSpan = 
        let p = NativeInterop.NativePtr.stackalloc<byte> 100 |> NativeInterop.NativePtr.toVoidPtr
        Span<byte>(p, 100)

    for i = 0 to stackSpan.Length - 1 do
        stackSpan[i] <- data
        data <- data + 1uy

    let mutable stackSum = 0
    for value in stackSpan do
        stackSum <- stackSum + int value

    printfn $"The sum is {stackSum}"
// Output:  The sum is 4950

Da Span<T> es sich um eine Abstraktion über einen beliebigen Speicherblock handelt, arbeiten Methoden des Typs und der Span<T> Methoden mit Span<T> Parametern unabhängig von der Art des Speichers, den sie kapselt, für jedes Span<T> Objekt. Beispielsweise können alle separaten Codeabschnitte, die die Spanne initialisieren und die Summe der zugehörigen Elemente berechnen, in einzelne Initialisierungs- und Berechnungsmethoden geändert werden, wie das folgende Beispiel veranschaulicht:

public static void WorkWithSpans()
{
    // Create a span over an array.
    var array = new byte[100];
    var arraySpan = new Span<byte>(array);

    InitializeSpan(arraySpan);
    Console.WriteLine($"The sum is {ComputeSum(arraySpan):N0}");

    // Create an array from native memory.
    var native = Marshal.AllocHGlobal(100);
    Span<byte> nativeSpan;
    unsafe
    {
        nativeSpan = new Span<byte>(native.ToPointer(), 100);
    }

    InitializeSpan(nativeSpan);
    Console.WriteLine($"The sum is {ComputeSum(nativeSpan):N0}");

    Marshal.FreeHGlobal(native);

    // Create a span on the stack.
    Span<byte> stackSpan = stackalloc byte[100];

    InitializeSpan(stackSpan);
    Console.WriteLine($"The sum is {ComputeSum(stackSpan):N0}");
}

public static void InitializeSpan(Span<byte> span)
{
    byte value = 0;
    for (int ctr = 0; ctr < span.Length; ctr++)
        span[ctr] = value++;
}

public static int ComputeSum(Span<byte> span)
{
    int sum = 0;
    foreach (var value in span)
        sum += value;

    return sum;
}
// The example displays the following output:
//    The sum is 4,950
//    The sum is 4,950
//    The sum is 4,950
open System
open System.Runtime.InteropServices
open FSharp.NativeInterop

// Package FSharp.NativeInterop.NativePtr.stackalloc for reuse.
let inline stackalloc<'a when 'a: unmanaged> length : Span<'a> =
    let voidPointer = NativePtr.stackalloc<'a> length |> NativePtr.toVoidPtr
    Span<'a>(voidPointer, length)

let initializeSpan (span: Span<byte>) =
    let mutable value = 0uy
    for i = 0 to span.Length - 1 do
        span[i] <- value
        value <- value + 1uy

let computeSum (span: Span<byte>) =
    let mutable sum = 0
    for value in span do
        sum <- sum + int value
    sum

let workWithSpans () =
    // Create a span over an array.
    let array = Array.zeroCreate<byte> 100
    let arraySpan = Span<byte> array

    initializeSpan arraySpan
    printfn $"The sum is {computeSum arraySpan:N0}"

    // Create an array from native memory.
    let native = Marshal.AllocHGlobal 100
    let nativeSpan = Span<byte>(native.ToPointer(), 100)

    initializeSpan nativeSpan
    printfn $"The sum is {computeSum nativeSpan:N0}"

    Marshal.FreeHGlobal native

    // Create a span on the stack.
    let stackSpan = stackalloc 100

    initializeSpan stackSpan
    printfn $"The sum is {computeSum stackSpan:N0}"

// The example displays the following output:
//    The sum is 4,950
//    The sum is 4,950
//    The sum is 4,950

Arrays

Wenn ein Array umbrochen wird, Span<T> kann ein gesamtes Array wie in den Beispielen im Abschnitt "Speicher " umbrochen werden. Da es das Slicing unterstützt, Span<T> kann er auch auf einen zusammenhängenden Bereich innerhalb des Arrays verweisen.

Im folgenden Beispiel wird ein Segment der mittleren fünf Elemente eines ganzzahligen Arrays mit 10 Elementen erstellt. Beachten Sie, dass der Code die Werte jeder ganzen Zahl im Segment verdoppelt. Wie die Ausgabe zeigt, werden die von der Spanne vorgenommenen Änderungen in den Werten des Arrays angezeigt.

using System;

var array = new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };
var slice = new Span<int>(array, 2, 5);
for (int ctr = 0; ctr < slice.Length; ctr++)
    slice[ctr] *= 2;

// Examine the original array values.
foreach (var value in array)
    Console.Write($"{value}  ");
Console.WriteLine();

// The example displays the following output:
//      2  4  12  16  20  24  28  16  18  20
module Program

open System

[<EntryPoint>]
let main _ =
    let array = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |]
    let slice = Span<int>(array, 2, 5)
    for i = 0 to slice.Length - 1 do
        slice[i] <- slice[i] * 2

    // Examine the original array values.
    for value in array do
        printf $"{value}  "
    printfn ""
    0
// The example displays the following output:
//      2  4  12  16  20  24  28  16  18  20

Slices

Span<T> enthält zwei Überladungen der Slice Methode, die einen Datenschnitt aus der aktuellen Spanne bilden, die bei einem angegebenen Index beginnt. Dadurch können die Daten in einer Span<T> Reihe von logischen Blöcken behandelt werden, die nach Bedarf von Teilen einer Datenverarbeitungspipeline mit minimalen Leistungseinbußen verarbeitet werden können. Da beispielsweise moderne Serverprotokolle häufig textbasiert sind, ist die Manipulation von Zeichenfolgen und Teilzeichenfolgen besonders wichtig. In der String Klasse ist Substringdie Hauptmethode zum Extrahieren von Teilzeichenfolgen . Für Datenpipelines, die auf umfangreiche Zeichenfolgenmanipulation angewiesen sind, bietet ihre Verwendung einige Leistungsstrafen, da sie:

  1. Erstellt eine neue Zeichenfolge, die die Teilzeichenfolge enthält.
  2. Kopiert eine Teilmenge der Zeichen aus der ursprünglichen Zeichenfolge in die neue Zeichenfolge.

Dieser Zuordnungs- und Kopiervorgang kann entweder oder Span<T>ReadOnlySpan<T>, wie im folgenden Beispiel gezeigt, eliminiert werden:

using System;

class Program2
{
    static void Run()
    {
        string contentLength = "Content-Length: 132";
        var length = GetContentLength(contentLength.ToCharArray());
        Console.WriteLine($"Content length: {length}");
    }

    private static int GetContentLength(ReadOnlySpan<char> span)
    {
        var slice = span.Slice(16);
        return int.Parse(slice);
    }
}
// Output:
//      Content length: 132
module Program2

open System

let getContentLength (span: ReadOnlySpan<char>) =
    let slice = span.Slice 16
    Int32.Parse slice

let contentLength = "Content-Length: 132"
let length = getContentLength (contentLength.ToCharArray())
printfn $"Content length: {length}"
// Output:
//      Content length: 132